package login

import (
	"github.com/fatih/color"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
	"gosh/pkg/connect"
	"os"
	"time"
)

var jumpServer *connect.JumpServer

func Run() {
	initJumpServer()
	login()
}

func initJumpServer() {
	if LoginConfig.BastionHost != "" {
		jumpServer = connect.NewJumpServer(LoginConfig.BastionHost, LoginConfig.BastionUser, LoginConfig.Password,
			LoginConfig.BastionKeyContent, LoginConfig.BastionKeyPassphrase, LoginConfig.BastionPort, LoginConfig.ConnectTimeout)
		err := jumpServer.Open()
		if err != nil {
			color.Red("connect to bastion %s@%s failed,err:%s", jumpServer.Username, jumpServer.IP, err.Error())
			os.Exit(1)
		}
	}
}

func login() {
	host := connect.NewHost(LoginConfig.Host, LoginConfig.User, LoginConfig.Password, LoginConfig.KeyContent,
		LoginConfig.KeyPassphrase, LoginConfig.Port, jumpServer, LoginConfig.ConnectTimeout)

	err := host.Open()
	if err != nil {
		color.Red("open ssh connection failed,err:%s\n", err.Error())
		return
	}

	go func() {
		t := time.NewTicker(5 * time.Second)
		defer t.Stop()
		for range t.C {
			_, _, err := host.SSHClient.Conn.SendRequest("keepalive@golang.org", true, nil)
			if err != nil {
				return
			}
		}
	}()

	session, err := host.SSHClient.NewSession()
	if err != nil {
		color.Red("new ssh session failed,err:%s\n", err.Error())
		return
	}
	defer session.Close()

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		color.Red("make terminal failed,err:%s\n", err.Error())
		return
	}
	defer terminal.Restore(fd, oldState)

	session.Stdout = os.Stdout
	session.Stderr = os.Stdin
	session.Stdin = os.Stdin

	termWidth, termHeight, err := terminal.GetSize(fd)
	if err != nil {
		color.Red("get terminal size failed,err:%s\n", err.Error())
		return
	}

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}
	err = session.RequestPty("xterm", termHeight, termWidth, modes)
	if err != nil {
		color.Red("request pty failed,err:%s\n", err.Error())
		return
	}
	err = session.Shell()
	if err != nil {
		color.Red("open shell failed,err:%s\n", err.Error())
		return
	}
	_ = session.Wait()
}
